#pragma once

#include <pthread.h>
#include <string>
#include <iostream>
#include <functional>

#define ERR(str) perror(str) 
    

namespace threadModule
{
    static size_t threadCount = 1;
    
    class thread
    {
        using Self = thread;
        using fun_t = std::function<void()>;
        
    private:

        void _enableRunning()
        {
            _isRunning = true;
        }

        void _enableDetach()
        {
            _isDetach = true;
        }

        static void* routine(void* args)
        {
            Self* self = (Self*)args;

            self->_enableRunning();
            if (self->_isDetach == true)
            {
                self->detach();
            }
            
            self->_fun();

            return nullptr;
        }

    public:

        thread(fun_t fun)
            :_tid(0)
            ,_isRunning(false)
            ,_isDetach(false)
            ,_fun(fun)
        {
            _name = "thread - " + std::to_string(threadCount++);
        }

        void start()
        {
            if (_isRunning == true)
            {
                return;
            }
            
            int num = pthread_create(&_tid, nullptr, routine, this);
            if (num != 0)
            {
                ERR("pthread_create");
            }
        }

        void join()
        {
            if (_isDetach == true)
            {
                return;
            }
            
            int num = pthread_join(_tid, nullptr);
            if (num != 0)
            {
                ERR("pthread_join");
            }
            else
            {
                std::cout << "thread join success" << std::endl;
            }
        }

        void stop()
        {
            if (_isRunning == false)
            {
                return;
            }
            
            int num = pthread_cancel(_tid);
            if (num != 0)
            {
                ERR("pthread_cancel");
            }
            else
            {
                std::cout << "thread stop success" << std::endl;
            }
        }

        void detach()
        {
            if (_isDetach == true)
            {
                return;
            }
            if (_isRunning == true)
            {
                int num = pthread_detach(_tid);
                if (num != 0)
                {
                    ERR("pthread_detach");
                }
                else
                {
                    std::cout << "thread detach success" << std::endl;
                }
            }
            
            _enableDetach();
        }

        ~thread()
        {
            ;
        }

    private:

        pthread_t _tid;
        std::string _name;
        bool _isRunning;
        bool _isDetach;
        fun_t _fun;
    };
}
